/*
 * BuildingExpert.cpp
 *
 *  Created on: Nov 21, 2008
 *      Author: Ryan
 */

#include "BuildingExpert.h"

#include "../UnitAnalyzers/GeneratorAnalyzer.h"
#include "../UnitAnalyzers/UnitCostAnalyzer.h"
#include "../UnitAnalyzers/ExtractorAnalyzer.h"
#include "../UnitAnalyzers/BuilderExtractor.h"
#include "../UnitAnalyzers/ResourceExtractorFilter.h"
#include "../UnitAnalyzers/ResourceGeneratorFilter.h"
#include "../UnitAnalyzers/FactoryFilter.h"
#include "../UnitAnalyzers/FactoryAnalyzer.h"
#include "../UnitAnalyzers/BuildingFilter.h"
#include "../ResourceAnalysisUnit.h"

#include "../../../OOInterface/Objects/Events/UnitCreatedEvent.h"
#include "../../../OOInterface/Objects/Units/UnitAspects/BuilderAspect.h"
#include "../../../OOInterface/Objects/Logger.h"

BuildingExpert::BuildingExpert( OOInterface::AIInterface* i, OOInterface::ConstPlayerPtr self, ResourceReservation* reserve ) :
	_i(i), _self(self), _reservation( reserve ), _infrastructure( i->getGameMap() )
{
}

BuildingExpert::~BuildingExpert()
{
}


void BuildingExpert::run()
{
	const OOInterface::ResourceList& resourceTypes = _i->getResourceTypes();

	ResourceMap availableResources;

	bool resourceImbalance = false;
	OOInterface::Resource imbalancedResource = resourceTypes.at(0);

	for( OOInterface::ResourceList::const_iterator resource = resourceTypes.begin(); resource != resourceTypes.end(); ++resource )
	{
		float available = _self->getResourceAmount( *resource );
		//OOInterface::Logger::getInstance("AI/TAI/TAI.txt") << "Available: " << resource->toString() << " " << available << std::endl;
		ResourceMap reserved = _reservation->getReservedResources();
		ResourceMap::iterator it = reserved.find( *resource );
		if( it != reserved.end() )
			available = available - abs( (*it).second );



		availableResources.insert( std::pair< const OOInterface::Resource, float> (*resource, available));

		if( available / _self->getResourceStorage( *resource ) < 0.25 || _self->getResourceUsage( *resource ) > _self->getResourceIncome( *resource )) {
			resourceImbalance = true;
			imbalancedResource = *resource;
		}
	}


	updateBuilderList();

	if( _idleBuilders.empty() )
		return;

	ResourceExtractorFilter filter1;
	updateList( filter1, _extractors, availableResources );
	ResourceGeneratorFilter filter2( _i->getResourceTypes() );
	updateList( filter2, _generators, availableResources );
	FactoryFilter filter3;
	updateList( filter3, _factories, availableResources );

	//OOInterface::Logger::getInstance("AI/TAI/TAI.txt") << "Factories(" << _factories.size()
	//<< ") Extractors(" << _extractors.size() << ") Generators(" << _generators.size() << ")" << std::endl;


	if( _idealExtractorLocations.empty() )
	{
		float maxRadius = findMaxExtractorRadius();

		_infrastructure.getBuiltBuildings();

		ResourceAnalysisUnit extractor(_i, resourceTypes.at(0), (*_idleBuilders.begin())->getLocation(), 75.0f, _builtExtractors, maxRadius);

		extractor.run();

		_idealExtractorLocations.push( extractor.getResult().front() );
	}


	if( !resourceImbalance && !_factories.empty() )
	{
		if( !factoryAndDefenses() )
			extractorsAndGenerators( imbalancedResource, resourceImbalance );
	}
	else
	{
		extractorsAndGenerators( imbalancedResource, resourceImbalance );
	}


}


bool BuildingExpert::factoryAndDefenses()
{
	FactoryAnalyzer factoryRater( _i, _self );

	float maxRating = 0.0f;
	OOInterface::ConstUnitPtr buildMe;

	for( OOInterface::ConstUnitSet::iterator building = _factories.begin(); building != _factories.end(); ++building )
	{
		factoryRater.reset();
		(*building)->accept( factoryRater );

		float buildingRating = factoryRater.getRating();

		if( buildingRating > maxRating )
		{
			maxRating = buildingRating; buildMe = (*building);
		}
	}



	if( buildMe.get() )
	{
		//OOInterface::Logger::getInstance("AI/TAI/TAI.txt") << "MaxRating: " << maxRating << " Building: " << buildMe->getName() << std::endl;
		OOInterface::Point3f loc = _infrastructure.findNearbyBuildLocation( buildMe, (*(_idleBuilders.begin()))->getLocation() );

		UnitCostAnalyzer costAnalyzer( _i->getResourceTypes() );
		costAnalyzer.reset();

		buildMe->accept( costAnalyzer );

		if( loc.getX() != -1 && loc.getZ() != -1 ) {
			giveOrder( *(_idleBuilders.begin()), _i->getCommandFactory().createBuildCommand( buildMe, loc ), costAnalyzer.getCost() );

			_infrastructure.buildBuilding( buildMe, loc );
			return true;
		}
	}

	return false;
}

bool BuildingExpert::extractorsAndGenerators( const OOInterface::Resource imbalanceResource, bool imbalance )
{

	if( imbalance )
	{

		GeneratorAnalyzer genRater( _i->getResourceTypes() );
		float maxRating = 0.0f;
		OOInterface::ConstUnitPtr buildMe;

		for( OOInterface::ConstUnitSet::iterator generator = _generators.begin(); generator != _generators.end(); ++generator )
		{
			genRater.reset();
			(*generator)->accept( genRater );

			if( maxRating < genRater.getRating() + genRater.getResourceGenerated( imbalanceResource ) )
			{
				maxRating = genRater.getRating() + genRater.getResourceGenerated( imbalanceResource );
				buildMe = *generator;
			}
		}

		if( buildMe.get() )
		{
			//OOInterface::Logger::getInstance("AI/TAI/TAI.txt") << "MaxRating: " << maxRating << " Building: " << buildMe->getName() << std::endl;
			OOInterface::Point3f loc = _infrastructure.findNearbyBuildLocation( buildMe, (*(_idleBuilders.begin()))->getLocation() );

			UnitCostAnalyzer costAnalyzer( _i->getResourceTypes() );
			costAnalyzer.reset();

			buildMe->accept( costAnalyzer );

			if( loc.getX() != -1 && loc.getZ() != -1 ) {
				giveOrder( *(_idleBuilders.begin()), _i->getCommandFactory().createBuildCommand( buildMe, loc ), costAnalyzer.getCost() );

				_infrastructure.buildBuilding( buildMe, loc );
				return true;
			}
		}
	}
	else
	{
		ExtractorAnalyzer extractorRater(_i, _self);

		float maxRating = 0.0f;
		OOInterface::ConstUnitPtr buildMe;

		for( OOInterface::ConstUnitSet::iterator extractor = _extractors.begin(); extractor != _extractors.end(); ++extractor )
		{
			if(!extractor->get())
				continue;

			extractorRater.reset( _idealExtractorLocations.front() );

			(*extractor)->accept( extractorRater );

			float rating = extractorRater.getRating();

			if( rating > maxRating )
			{
				maxRating = rating;
				buildMe = *extractor;
			}

		}

		if( buildMe.get() )
		{
			//OOInterface::Logger::getInstance("AI/TAI/TAI.txt") << "MaxRating: " << maxRating << " Building: " << buildMe->getName() << std::endl;
			OOInterface::Point3f loc = _infrastructure.findNearbyBuildLocation( buildMe, _idealExtractorLocations.front() );

			UnitCostAnalyzer costAnalyzer( _i->getResourceTypes() );
			costAnalyzer.reset();

			buildMe->accept( costAnalyzer );


			if( loc.getX() != -1 && loc.getZ() != -1 ) {
				giveOrder( *(_idleBuilders.begin()), _i->getCommandFactory().createBuildCommand( buildMe, loc ), costAnalyzer.getCost() );

				_builtExtractors.push( loc );
				_infrastructure.buildBuilding( buildMe, loc );
				_idealExtractorLocations.pop();
				return true;
			}
		}
	}

	return false;
}

void BuildingExpert::updateBuilderList()
{
	_idleBuilders.clear();

	OOInterface::ConstUnitSet allUnits = _self->getUnits();

	//Sift out all the builder units that are idle and unreserved from the AI's unit set.

	BuilderExtractor builderSifter;

	for( OOInterface::ConstUnitSet::iterator unit = allUnits.begin(); unit != allUnits.end(); ++unit )
	{
		if(!(*unit).get())
			continue;

		builderSifter.reset();
		(*unit)->accept( builderSifter );

		if( builderSifter.isBuilder() && (*unit)->isIdle() && (*unit)->isBuilt() && !_reservation->isBuilderReserved( *unit ) )
			_idleBuilders.insert( (*unit ) );
	}
}

void BuildingExpert::updateList( FilterVisitor& filter, OOInterface::ConstUnitSet& list, ResourceMap available )
{
	list.clear();

	BuilderExtractor builderSifter;
	UnitCostAnalyzer costAnalyzer( _i->getResourceTypes() );

	for( OOInterface::ConstUnitSet::iterator builder = _idleBuilders.begin(); builder != _idleBuilders.end(); ++builder )
	{
		if(!builder->get())
			continue;

		builderSifter.reset();
		(*builder)->accept( builderSifter );

		if( builderSifter.getAspect() )
		{
			const OOInterface::ConstUnitSet& buildable = builderSifter.getAspect()->getBuildableUnits();

			for( OOInterface::ConstUnitSet::const_iterator building = buildable.begin(); building != buildable.end(); ++building )
			{
				if(!building->get())
					continue;

				filter.reset();
				(*building)->accept( filter );

				costAnalyzer.reset();
				(*building)->accept( costAnalyzer );
				if( !filter.isFiltered() && costAnalyzer.canAfford( available ) )
					list.insert( (*building ) );
			}
		}

	}
}

float BuildingExpert::findMaxExtractorRadius()
{
	ResourceExtractorFilter extractor;
	extractor.reset();

	float maxRadius = 0.0f;

	for( OOInterface::ConstUnitSet::iterator unit = _extractors.begin(); unit != _extractors.end(); ++unit )
	{
		if( !unit->get() )
			continue;

		extractor.reset();
		(*unit)->accept( extractor );

		if( extractor.getExtractorAspect() && extractor.getExtractorAspect()->getRadius() > maxRadius )
			maxRadius = extractor.getExtractorAspect()->getRadius();
	}

	return maxRadius;
}

void BuildingExpert::giveOrder( OOInterface::ConstUnitPtr builder, OOInterface::ConstCommandPtr command, ResourceMap cost )
{
	_reservation->reserve( builder, command, cost );
	_i->orderUnit( builder , command );
}

